#ifndef __ICE_STUN_H
#define __ICE_STUN_H
#include <vector>
#include <string>
#include <memory>
#include <rtc_base/byte_buffer.h>
#include <rtc_base/socket_address.h>
namespace xrtc
{
    const size_t k_stun_header_size = 20;
    const size_t k_stun_attribute_header_size = 4;
    const size_t k_stun_transaction_id_offset = 8;
    const size_t k_stun_transaction_id_length = 12;
    const uint32_t k_stun_magic_cookie = 0x2112A442;
    const size_t k_stun_magic_cookie_length = sizeof(uint32_t);
    const size_t k_stun_message_integrity_size = 20;
    const uint32_t k_stun_type_mask = 0x0110;
    enum StunMessageType
    {
        STUN_BINDING_REQUEST = 0X0001,
        STUN_BINDING_RESPONSE = 0x0101,
        STUN_BINDING_ERROR_RESPONSE = 0x0111,

    };
    enum StunAttributeValue
    {

        STUN_ATTR_USERNAME = 0x0006,
        STUN_ATTR_MESSAGE_INTEGRITY = 0x0008,
        STUN_ATTR_ERROR_CODE = 0x0009,
        STUN_ATTR_XOR_MAPPED_ADDRESS = 0x0020,
        STUN_ATTR_PRIORITY = 0x0024,
        STUN_ATTR_USE_CANDIDATE = 0x0025,
        STUN_ATTR_FINGERPRINT = 0x8028,
        STUN_ATTR_ICE_CONTROLLING = 0x802A,

    };

    enum StunAttributeValueType
    {

        STUN_VALUE_UNKNOWN = 0,
        STUN_VALUE_UINT32,
        STUN_VALUE_BYTE_STRING,

    };
    enum
    {
        STUN_ERROR_BAD_REQUEST = 400,
        STUN_ERROR_UNAUTHORIZED = 401,
        STUN_ERROR_UNKNOWN_ATTRIBUTE = 420,
        STUN_ERROR_SERVER_ERROR = 500,
        STUN_ERROR_GLOBAL_FAIL = 600,

    };
    enum StunAddressFamily
    {
        STUN_ADDRESS_UNDEF = 0,
        STUN_ADDRESS_IPV4 = 1,
        STUN_ADDRESS_IPV6 = 2,

    };
    extern const char STUN_ERROR_REASON_BAD_REQUEST[];
    extern const char STUN_ERROR_REASON_UNAUTHORIZED[];
    extern const char STUN_ERROR_REASON_SERVER_ERROR[];

    class StunAttribute;
    class StunByteStringAttribute;
    class StunUInt32Attribute;
    class StunErrorCodeAttribute;

    std::string StunMethodToString(int type);
    class StunMessage
    {
    public:
        enum class IntegrityStatus
        {
            kNotSet,
            kNotIntegrity,
            kIntegrityOk,
            kIntegrityBad,

        };
        StunMessage();
        ~StunMessage();

        int Type() const { return type_; }
        size_t Length() const { return length_; }
        void SetType(uint16_t type) { type_ = type; }
        void SetLength(uint16_t length) { length_ = length; }
        void SetTransationId(const std::string &transation_id) { transcation_id_ = transation_id; };
        bool AddMessageIntegrity(const std::string &password);
        bool AddFingerPrint();
        void AddAttribute(std::unique_ptr<StunAttribute> attr);
        const StunErrorCodeAttribute* GetErrorCode();
        int GetErrorCodeValue();

        const std::string &TranscationId() const { return transcation_id_; }

        static bool ValidateFingerprint(const char *data, size_t len);
        IntegrityStatus ValidateMessageIntegrity(const std::string &password);

        bool Read(rtc::ByteBufferReader *buf);
        bool Write(rtc::ByteBufferWriter *buf) const;
        StunAttributeValueType GetAttributeValueType(int type);
        // 获取子类的指针
        const StunByteStringAttribute *GeByteString(uint16_t type);
        // protity
        const StunUInt32Attribute *GetUint32(uint16_t type);
        bool AddMessageIntegrityOfType(uint16_t attr_type, uint16_t attr_size, const char *key, size_t len);

        IntegrityStatus Integrity() {return integrity_;}
        bool IntegrityOk() {return integrity_ == IntegrityStatus::kIntegrityOk;}

    private:
        StunAttribute *CreateAttribute(uint16_t type, uint16_t length);
        const StunAttribute *GetAttribute(uint16_t type);
        bool ValidateMessageIntegrityOfType(uint16_t mi_attr_type, size_t mi_attr_size, const char *data, size_t size, const std::string &paddword);

    private:
        uint16_t type_;
        uint16_t length_;
        std::string transcation_id_;
        std::vector<std::unique_ptr<StunAttribute>> attrs_;
        // integrity
        IntegrityStatus integrity_ = IntegrityStatus::kNotSet;
        std::string password_;
        std::string buffer_;
    };
    class StunAttribute
    {
    public:
        virtual ~StunAttribute();
        virtual bool Read(rtc::ByteBufferReader *buf) = 0;
        virtual bool Write(rtc::ByteBufferWriter *buf) = 0;
        int Type() const { return type_; }
        size_t Length() const { return length_; }
        void SetType(uint16_t type) { type_ = type; }
        void SetLength(uint16_t length) { length_ = length; }
        static std::unique_ptr<StunErrorCodeAttribute> CreateErrorCode();

        static StunAttribute *
        Create(StunAttributeValueType value_type, uint16_t type, uint16_t length, void *ower);

    protected:
        StunAttribute(uint16_t type, uint16_t length);
        void ConsumePadding(rtc::ByteBufferReader *buf);
        void WritePadding(rtc::ByteBufferWriter *buf);

    private:
        uint16_t type_;
        uint16_t length_;
    };

    class StunAddressAttribute : public StunAttribute
    {
    public:
        static const size_t SIZE_UNDEF = 0;
        static const size_t SIZE_IPV4 = 8;
        static const size_t SIZE_IPV6 = 20;

        StunAddressAttribute(uint16_t type, const rtc::SocketAddress &addr);
        ~StunAddressAttribute() {}
        bool Read(rtc::ByteBufferReader *buf) override;
        bool Write(rtc::ByteBufferWriter *buf) override;
        void SetAddress(const rtc::SocketAddress &addr);
        StunAddressFamily Family();

    protected:
        rtc::SocketAddress address_;
    };
    class StunXorAddressAttribute : public StunAddressAttribute
    {
    public:
        StunXorAddressAttribute(uint16_t type, const rtc::SocketAddress &addr);
        ~StunXorAddressAttribute() {}
        bool Read(rtc::ByteBufferReader *buf) override;
        bool Write(rtc::ByteBufferWriter *buf) override;

    private:
        rtc::IPAddress GetXoredIp();
    };

    class StunUInt32Attribute : public StunAttribute
    {

    public:
        static const size_t SIZE = 4;
        StunUInt32Attribute(uint16_t type);
        StunUInt32Attribute(uint16_t type, uint32_t value);
        ~StunUInt32Attribute() override {}
        bool Read(rtc::ByteBufferReader *buf) override;
        bool Write(rtc::ByteBufferWriter *buf) override;

        uint32_t Value() const { return bits_; }
        void SetValue(uint32_t value) { bits_ = value; }

    private:
        uint32_t bits_;
    };

    class StunUInt64Attribute : public StunAttribute
    {

    public:
        static const size_t SIZE = 8;
        StunUInt64Attribute(uint16_t type);
        StunUInt64Attribute(uint16_t type, uint64_t value);
        ~StunUInt64Attribute() override {}
        bool Read(rtc::ByteBufferReader *buf) override;
        bool Write(rtc::ByteBufferWriter *buf) override;

        uint64_t Value() const { return bits_; }
        void SetValue(uint64_t value) { bits_ = value; }

    private:
        uint64_t bits_;
    };

    class StunByteStringAttribute : public StunAttribute
    {
    public:
        StunByteStringAttribute(uint16_t type, uint16_t length);
        StunByteStringAttribute(uint16_t type, const std::string &str);

        ~StunByteStringAttribute();
        void CopyBytes(const char *bytes, size_t len);

        bool Read(rtc::ByteBufferReader *buf) override;
        bool Write(rtc::ByteBufferWriter *buf) override;

        std::string GetString() const { return std::string(bytes_, Length()); }

    private:
        void SetBytes(char *bytes);

    private:
        char *bytes_ = nullptr;
    };
    class StunErrorCodeAttribute : public StunAttribute
    {

    public:
        static const uint16_t MIN_SIZE;
        StunErrorCodeAttribute(uint16_t type, uint16_t length);
        ~StunErrorCodeAttribute() override = default;
        int Code() const;
        void SetCode(int code);
        void SetReason(const std::string &reason);
        bool Read(rtc::ByteBufferReader *buf) override;
        bool Write(rtc::ByteBufferWriter *buf) override;

    private:
        uint8_t class_;
        uint8_t number_;
        std::string reason_;
    };

int GetStunSuccessResponse(int req_type);
int GetStunErrorResponse(int req_type);
bool IsStunRequestType(int req_type);


} // namespace xrtc

#endif